React hook 是 React V16.8 新增的功能,它改變了 React 寫 Class Component 的習慣,轉而改成寫 Functional Component,早期的 React 元件事實上就區分為 Class 跟 Functional兩種,但是 Class 通常是可以控制 State 跟 生命週期的而 Functional 主要是用來呈現 UI 的部分,而 Hook 打破這層限制,統一使用 Functional Component,直接在內部就可以管理 State。
其實 Hook 很像 JS 的 Module Pattern,Module Pattern 是 JS 利用函數閉包(Closure)的特性,藉由閉包實現封裝的功能,將變數和方法限制在範圍內使用,達到避免(變數、功能)全局污染的一種方法。
因為 Javascript 原生並沒有 private, public, protected 的概念,而是依靠作用域,所以要切分開來只能依靠閉包。(不清楚可以看:【Day 6】關於ES6作用域)
閉包(Closure):閉包是指變數的生命週期只存在在該 function 中,如果離開了 function,變數會自動被回收而且不可在使用,而且必須要事先在 function 中宣告。
Module Pattern: 就是利用閉包的特性,將變數和函數限制在特定範圍內使用
舉例來說:
var exampleModule = (function(){
var counter = 0;
return {
increase: function() {
return counter += 1;
},
decrease: function() {
return counter -= 1;
}
}
}());
console.log(exampleModule.increase()); // 1
console.log(exampleModule.decrease()); // 0
原始寫法:
import React, { Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = { count:0 }
this.increase = this.increase.bind(this);
}
increase(){
this.setState({count:this.state.count+1})
}
render() {
return (
<div>
<p>你已經點了 {this.state.count} 次</p>
<button onClick={this.increase}>點我</button>
</div>
);
}
}
export default Example;
React Hook 寫法:
import React, { useState } from 'react';
function Example(){
const [ count , setCount ] = useState(0);
return (
<div>
<p>你已經點了 {count} 次</p>
<button onClick={()=>{setCount(count+1)}}>點我</button>
</div>
)
}
export default Example;
這裡要注意,要使用 React Hook 並不需要另外安裝套件,而是 React 16.8 以上的內建功能。
較接近原生的 js 寫法,對於剛開始接觸的人有好處,且不需要懂 ES6 也可以寫
減少了解太過多餘的元件週期,只要控制好 useEffect 即可
用相對簡單的寫法解決複雜的問題,這點尤其重要
1.元件更好重複利用
2.程式碼更簡潔
當下面這段程式運行的時候,可以試著先點擊“3 秒後跳出值”的按鈕,在去點擊“增加”的按鈕,就會發現最後Alert 的數字是當時“點擊3 秒後跳出值”的當前數值,而不是最後的增加後的數值。這是因為異步操作時,所引用的變量是之前的(作用域與閉包概念),但是這問題在 Class Component 就不會發生,因為 Class Component 的屬性都放在一個 instance 上,並且使用 this.state.xxx 和 this.method() 進行調用,而每次都是在同一個 instance 上取值,所以沒有所謂之前的值的問題。
import React, { useState } from "react";
const Example = () => {
const [counter, setCounter] = useState(0);
const onAlertButtonClick = () => {
setTimeout(() => {
alert("最後顯示值: " + counter);
}, 3000);
};
return (
<div>
<p>你已經點了 {counter} 次</p>
<button onClick={() => setCounter(counter + 1)}>增加</button>
<button onClick={onAlertButtonClick}>
3 秒後跳出值
</button>
</div>
);
};
export default Example;